#ifndef COMP_H
#define COMP_H
/*
 *  Copyright 2022-2025 Michael T. Richter as Half-Baked Software
 *  This program is free software. It comes without any warranty, to the extent
 *  permitted by applicable law. You can redistribute it and/or modify it under
 *  the terms of the Do What The Fuck You Want To Public License, Version 2, as
 *  published by Sam Hocevar. See the file COPYING or http://www.wtfpl.net/ for
 *  more details.
 */
/** @file
 *  @brief COMP component of the COROS coroutine-OS.
 *
 *  COMP is more of a design guideline for proper paired use of CO and EVQ to
 *  provide a Kay-style OOP environment for embedded systems.  For this reason
 *  it is almost all provided in a header file and most of this header file is
 *  a design essay followed by some helper macros that will provide default
 *  implementations of the COROS COMP approach.
 *
 *  Kay-style OOP
 *  =============
 *
 *  The person who coined the term "object-oriented programming" did not
 *  envision the monstrosities that are C++, Java, C#, et al.  In Alan Kay's
 *  vision, an "object" was an independent software entity that responded to
 *  "messages".  The fact that his language--Smalltalk--had things like
 *  inheritance and such was an implementation detail.  Sadly the industry took
 *  away the wrong lesson and mimicked the form of Smalltalk without looking at
 *  its substance.  The result was a common (almost ubiquitous) paradigm that
 *  actually managed to be worse than what it was trying to replace.
 *
 *  COROS is an attempt to go back to the roots of OOP.  There are no
 *  inheritance trees.  There are no externally-visible member variables,
 *  member functions, etc.  There is just independent islands of computation
 *  who receive messages and respond to them.  In COROS these are referred to
 *  as "components" and are built up from coroutines (via CO) and events (via
 *  EVQ).
 *
 *  Components
 *  ==========
 *
 *  A component in COMP typically has the following pieces:
 *
 *  1. A private event queue hooked into a parent queue.
 *  2. A private coroutine that forms its "message pump".
 *  3. A public API that gives a familiar function-calling interface to the
 *     coroutine/message system underlying the component.
 *
 *  Not all components require all pieces of this.  A "heartbeat" component,
 *  for example, might not really need a coroutine or even an event queue.
 *  Whatever the case, however, the user should not be required to know what
 *  pieces a component has or does not have.  The implementation should be a
 *  black box.
 *
 *  A component is typically placed in a single .c/.h pair.  The .h file
 *  exposes the public interface of the component, and the .c file provides its
 *  implementation.  A compliant component *must* expose the following three
 *  functions:
 *
 *      `<component>_init()`        - hook up the component to the queue tree
 *                                    and start its processing if applicable.
 *      `<component>_deactivate()`  - stop processing events.
 *      `<component>_activate()`    - start processing events again.
 *
 *  A component *may* also expose any API it wishes.  This could be as
 *  primitive as a single function to send messages plus a variety of message
 *  structures to fill for processing, or it could be as complicated as a set
 *  of functions that helpfully wrap such a single primitive.  (It is strongly
 *  recommended that the latter be used.)
 *
 *  Functions exposed by COMP components will fall into three broad types:
 *
 *  1. Wrappers that send a message and return immediately (like event
 *     notifications).
 *  2. Wrappers that send a message and wait for the operation to complete,
 *     possibly with a return value (like procedure or function calls).
 *  3. Functions that manipulate static state in the component and return
 *     immediately without sending messages (usually checking or altering
 *     internal state).
 *
 *  From the perspective of a component user, there should be no visible
 *  difference in *use* of these three types (though there will be clear
 *  differences in performance and behaviour).
 */

#include "co.h"
#include "evq.h"
#include "evq_impl.h"

/*! Type definition for pump body return values.
 *
 *  This enumeration type contains symbolic definitions for what the pump body
 *  of a component will return to signal the pump's main loop.
 */

typedef enum _comp_body_t
{
    COMP_TERMINATE,
    COMP_CONTINUE,
} comp_body_t;

/*! Helper macro to get rid of undesired warnings. */
#ifndef UNUSED
#define UNUSED(X) ((void)(X))
#endif

/*! Declare the required publicly-visible functions of a COMP instance.
 *
 *  This is typically provided in the <component>.h file as one of the
 *  first declarations.  It exposes:
 *      `<component>_activate()`
 *      `<component>_deactivate()`
 *      `<component>_init()`
 *
 *  `<component>_activate()` inserts the component's event queue into the
 *  parent queue's chain, rendering the component live.  (Note that components
 *  are started live: this function is only useful if the component has first
 *  been deactivated.)
 *  `<component_deactivate()` removes the component's event queue from the
 *  parent queue's chain, rendering the component inert.
 *  `<component>_init()` takes a component master queue and a `void *` of
 *  initialization data to pass into the component's coroutine.  Inside the
 *  function a default component will build the component's private event
 *  queue, attach it to the master queue, create the component's message
 *  processing coroutine (called its 'pump'), and then start the coroutine,
 *  passing in the provided data.
 */
#define COMP_DECLARE(NAME)                   \
extern void NAME##_activate(void);           \
extern void NAME##_deactivate(void);         \
extern void NAME##_init(evq_queue_t, void *)

/*! Define a standard COROS component.
 *
 *  Most COROS components are going to be very similar in architecture, thus
 *  this macro is provided.  It is invoked by giving it a name and a stack size
 *  (in words).  It generates a coroutine variable, a local event queue handle,
 *  a copy of the master event queue handle, and a default implementation of
 *  the _activate(), _deactivate(), and _init() functions.  It also supplies a
 *  default implementation of the event pump coroutine that is structured as
 *  an initialization callback, a do-loop that yields, and then calls a body
 *  callback on each iteration (terminating if the body returns 'false'), and
 *  a termination callback that is called when the body exits (by returning
 *  false).
 *
 *  All three callbacks must be provided by the user.  In the frequent case of
 *  the callbacks not having any functionality, a simple empty body suffices.
 *
 *  When making a standard COROS component, then, this macro is invoked and
 *  three callback functions must be given an implementation.
 *
 *  Further, a handful of utility functions are provided that perform common
 *  operations across components.  These are:
 *
 *      <component>_call(void *);   - resume the component coroutine with
 *                                    passed data
 *      <component>_delay(const delay_ms);
 *                                  - non-blocking ms delay
 *      <component>_signal(void);   - resume the component coroutine with no
 *                                    data
 *      <component>_yield();        - yield a time slice
 */
#define COMP_DEFINE(NAME, STACK)                                \
static co_t NAME##_co;                                          \
static evq_queue_t NAME##_queue;                                \
static evq_queue_t NAME##_master_queue;                         \
static struct _evq_event_t NAME##_self;                         \
static void *NAME##_call(void *);                               \
static void NAME##_delay(const delay_ms);                       \
static void NAME##_pump(void *);                                \
static void NAME##_send(void *);                                \
static void NAME##_signal(void);                                \
static void NAME##_yield(void);                                 \
void NAME##_activate(void) {                                    \
    evq_attach_queue(NAME##_master_queue, NAME##_queue);        \
}                                                               \
void NAME##_deactivate(void) {                                  \
    evq_detach_queue(NAME##_queue);                             \
}                                                               \
void NAME##_init(evq_queue_t m, void *data) {                   \
    NAME##_queue = evq_create_queue();                          \
    NAME##_master_queue = m;                                    \
    NAME##_activate();                                          \
    NAME##_co = co_create(NAME##_pump, STACK, NULL, NULL);      \
    evq_fill_co_event(&NAME##_self, 0, 0, NAME##_co, NULL);     \
    co_start(NAME##_co, data);                                  \
}                                                               \
static void NAME##_pump_x_init(void *data);                     \
static comp_body_t NAME##_pump_x_body(void *data);              \
static void NAME##_pump_x_terminate(void *data);                \
static void NAME##_pump(void *data) {                           \
    UNUSED(NAME##_call);                                        \
    UNUSED(NAME##_delay);                                       \
    UNUSED(NAME##_send);                                        \
    UNUSED(NAME##_signal);                                      \
    UNUSED(NAME##_yield);                                       \
    NAME##_pump_x_init(data);                                   \
    do {                                                        \
        data = co_yield(data);                                  \
    } while (NAME##_pump_x_body(data) == COMP_CONTINUE);        \
    NAME##_pump_x_terminate(data);                              \
}                                                               \
static inline void *NAME##_call(void *param) {                  \
    return co_resume(NAME##_co, param);                         \
}                                                               \
static inline void NAME##_send(void *param) {                   \
    evq_resume_now(NAME##_queue, NAME##_co, param);             \
}                                                               \
static inline void NAME##_signal(void) {                        \
    NAME##_send(NULL);                                          \
}                                                               \
static void NAME##_yield(void) {                                \
    NAME##_signal();                                            \
    co_yield(NULL);                                             \
}                                                               \
static inline void NAME##_delay(const delay_ms ms) {            \
    evq_process_for(NAME##_master_queue, ms, NULL);             \
}

#endif //!COMP_H
